// 在上次打劫完一条街道之后和一圈房屋后，小偷又发现了一个新的可行窃的地区。这个地区只有一个入口，我们称之为“根”。 除了“根”之外，每栋房子有且只有一个“父“房子与之相连。一番侦察之后，聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫，房屋将自动报警。

// 计算在不触动警报的情况下，小偷一晚能够盗取的最高金额。

// 示例 1:

// 输入: [3,2,3,null,3,null,1]

//      3
//     / \
//    2   3
//     \   \ 
//      3   1

// 输出: 7 
// 解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
// 示例 2:

// 输入: [3,4,5,1,3,null,1]

//      3
//     / \
//    4   5
//   / \   \ 
//  1   3   1

// 输出: 9
// 解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

#include <unordered_map>

using namespace std;

// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

/* 动态规划，dfs，哈希表
时间复杂度：O(n)
空间复杂度：O(n)
*/
class Solution {
public:
    int rob(TreeNode* root) {
        dfs(root);
        return max(f[root], g[root]);
    }
    void dfs(TreeNode* root) {
        if ( nullptr == root ) return;
        dfs(root->left);
        dfs(root->right);
        f[root] = root->val + g[root->left] + g[root->right];
        g[root] = max(f[root->left], g[root->left]) + max(f[root->right], g[root->right]);
    }
private:
    unordered_map<TreeNode*, int> f{}; // 选择该节点，子树最大金额
    unordered_map<TreeNode*, int> g{}; // 不选该节点，子树最大金额
};

/* 动态规划，dfs
时间复杂度：O(n)
空间复杂度：O(n)，调用栈空间
*/
class Solution {
private:
    struct SubtreeStatus {
        int selected;
        int notselected;
    };
public:
    int rob(TreeNode* root) {
        SubtreeStatus rootStatus = dfs(root);
        return max(rootStatus.selected, rootStatus.notselected);
    }
    SubtreeStatus dfs(TreeNode* root) {
        if ( nullptr == root ) return {0, 0};
        SubtreeStatus l = dfs(root->left);
        SubtreeStatus r = dfs(root->right);
        int selected = root->val + l.notselected + r.notselected;
        int notselected = max(l.selected, l.notselected) + max(r.selected, r.notselected);
        return {selected, notselected};
    }
};